package simplelog

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"time"
)

const (
	B uint64 = 1 << (10 * iota)
	K
	M
	G
	T
	P
)

// 自定义日志

type LogLevel struct {
	s    string
	flag LevelFlag
}

func (l LogLevel) String() string {
	return l.s
}

type LevelFlag int

const (
	FlagInfo  LevelFlag = 0x1
	FlagWarn  LevelFlag = 0x2
	FlagError LevelFlag = 0x4
	FlagDebug LevelFlag = 0x8
)

var (
	INFO  = LogLevel{"INFO", FlagInfo}
	WARN  = LogLevel{"WARN", FlagWarn}
	ERROR = LogLevel{"ERROR", FlagError}
	DEBUG = LogLevel{"DEBUG", FlagDebug}
)

// Logger 日志类
type Logger struct {
	fn           string
	w            io.Writer
	index        int
	splitSize    uint64
	timeFormat   string
	loggerFormat string
	flag         LevelFlag
}

func (l *Logger) time() string {
	now := time.Now()
	return now.Format(l.timeFormat)
}

func (l *Logger) PrintLog(level LogLevel, log string) {
	// 根据 flag 位 限制日志级别
	if l.flag&level.flag != level.flag {
		return
	}

	for {
		if len(l.fn) == 0 {
			break
		} else {
			file := l.w.(*os.File)
			info, _ := file.Stat()
			currentSize := uint64(info.Size())
			if l.splitSize == 0 || currentSize < l.splitSize {
				break
			} else {
				_ = file.Close()
				for {
					file, err := os.OpenFile(fmt.Sprintf("%s.%d", l.fn, l.index), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
					l.index++
					if err != nil {
						continue
					} else {
						l.w = file
						break
					}
				}
			}
		}
	}

	pc, _, _, ok := runtime.Caller(2)
	var f *runtime.Func
	if ok {
		f = runtime.FuncForPC(pc)
		_, _ = fmt.Fprintf(l.w, l.loggerFormat, l.time(), level, pc, f.Name(), log)
	} else {
		_, _ = fmt.Fprintf(l.w, l.loggerFormat, l.time(), level, "--", "Unknown", log)
	}
}

// Debug 调试输出
func (l *Logger) Debug(log string) {
	l.PrintLog(DEBUG, log)
}

// Info 消息输出
func (l *Logger) Info(log string) {
	l.PrintLog(INFO, log)
}

// Warn 消息输出
func (l *Logger) Warn(log string) {
	l.PrintLog(WARN, log)
}

// Error 消息输出
func (l *Logger) Error(log string) {
	if l.flag&FlagError != FlagError {
		return
	}

	pc, file, line, ok := runtime.Caller(1)
	l.PrintLog(ERROR, log)
	if ok {
		_, _ = fmt.Fprintf(l.w, "=> %d %s\tline: %d\n", pc, file, line)
	} else {
		_, _ = fmt.Fprintf(l.w, "=> Unknown Unknown : Unknown\n")
	}
}

// 函数式 选项模式

type Option struct {
	Writer     io.Writer
	TimeFormat string
	LogFormat  string
	LevelFlag  LevelFlag
	LogFile    string
	SplitSize  uint64
}

type OptionFunc func(*Option)

func WithWriter(writer io.Writer) OptionFunc {
	return func(option *Option) {
		if len(option.LogFile) == 0 {
			option.Writer = writer
		}
	}
}

func WithTimeFormat(timeFormat string) OptionFunc {
	return func(option *Option) {
		option.TimeFormat = timeFormat
	}
}

func WithLogFormat(logFormat string) OptionFunc {
	return func(option *Option) {
		option.LogFormat = logFormat
	}
}

func WithLogFlag(flag LevelFlag) OptionFunc {
	return func(option *Option) {
		option.LevelFlag = flag
	}
}

func WithLogFile(filePath string) OptionFunc {
	return func(option *Option) {
		if len(filePath) > 0 {
			option.LogFile = filePath
			file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
			if err != nil {
				panic(err)
			}
			option.Writer = file
		}
	}
}

func WithSplitSize(size uint64) OptionFunc {
	return func(option *Option) {
		option.SplitSize = size
	}
}

// 默认参数
var defaultOption = &Option{
	Writer:     os.Stdout,
	TimeFormat: "2006-01-02 15:04:05.99",
	LogFormat:  "%-22s %-5s %v --- [%20s] : %s\n",
	LevelFlag:  FlagInfo | FlagWarn | FlagError | FlagDebug,
	LogFile:    "",
	SplitSize:  0,
}

// NewOption 构造 Option 参数
func NewOption(opts ...OptionFunc) (opt *Option) {
	opt = defaultOption
	for _, o := range opts {
		o(opt)
	}
	return
}

// NewLog 构造函数
func NewLog(opts ...OptionFunc) *Logger {
	opt := NewOption(opts...)

	return &Logger{
		w:            opt.Writer,
		timeFormat:   opt.TimeFormat,
		loggerFormat: opt.LogFormat,
		flag:         opt.LevelFlag,
		fn:           opt.LogFile,
		splitSize:    opt.SplitSize,
	}
}
